package jp.sourceforge.akjrcp.ffmpeg;

import com.akjava.lib.standards.process.ProcessMonitor;
import com.akjava.lib.standards.process.SimpleProcessPrinter;

import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.math.BigDecimal;
import java.util.HashMap;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.AudioInputStream;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.UnsupportedAudioFileException;
import javax.sound.sampled.AudioFormat.Encoding;

import jp.sourceforge.akjrcp.util.AvisynthUtils;

import org.apache.commons.lang.StringUtils;

 public  final class FFMpegUtils {

		public static boolean isAudioOnlyExtension(String formatOrExtension){
			if(formatOrExtension==null){
				return false;
			}
			String audios[]={"mp2","mp3","aac","wav","lpcm","ac3","wav","wma","m2a"};
			for (int i = 0; i < audios.length; i++) {
				if(formatOrExtension.equals(audios[i])){
					return true;
				}
			}
					
					return false;
		}
		
		public static String getDefaultAudioCodec(String format,String extension){
			if(format!=null){
				String codec= getAudioCodecByFormat(format);
				if(codec!=null){
					return codec;
				}else{
					//wrong format?
				}
			}
			
			if(extension!=null){
				return getAudioCodecByExtension(extension);
			}
			
			
			return null;
		}
		
		public static String getDefaultVideoCodec(String format,String extension){
			if(format!=null){
				String codec= getVideoCodecByFormat(format);
				if(codec!=null){
					return codec;
				}else{
					//wrong format?
				}
			}
			
			if(extension!=null){
				return getVideoCodecByExtension(extension);
			}
			
			
			return null;
		}
		

		
		
		public static String getVideoCodecByFormat(String format){
			 Map<String,String> map=new HashMap<String,String>();
			 map.put("mp4", FFMpegOptions.CODEC_VIDEO_MPEG4);	//but this is not supported pure_ffmpeg
			 map.put("avi", FFMpegOptions.CODEC_VIDEO_MPEG4);
			 map.put("mpeg", FFMpegOptions.CODEC_VIDEO_MPEG1);
			 map.put("vob", FFMpegOptions.CODEC_VIDEO_MPEG2);
			 
	
			 map.put("dvd", FFMpegOptions.CODEC_VIDEO_MPEG2);
			 map.put("mov", FFMpegOptions.CODEC_VIDEO_MPEG4);
			 map.put("3gp",  FFMpegOptions.CODEC_VIDEO_H263);
			 map.put("3g2",FFMpegOptions.CODEC_VIDEO_H263);
			
			 return map.get(format);
		 }
		
		public static String getVideoCodecByExtension(String format){
			 Map<String,String> map=new HashMap<String,String>();
			 map.put("mp4", FFMpegOptions.CODEC_VIDEO_MPEG4);	//but this is not supported pure_ffmpeg
			 map.put("avi", FFMpegOptions.CODEC_VIDEO_MPEG4);
			 map.put("vob", FFMpegOptions.CODEC_VIDEO_MPEG2);
			 map.put("wmv", FFMpegOptions.CODEC_VIDEO_MSMPEG4);
			 map.put("mpg", FFMpegOptions.CODEC_VIDEO_MPEG1);
			 
			 map.put("mov", FFMpegOptions.CODEC_VIDEO_MPEG4);
			 map.put("3gp",  FFMpegOptions.CODEC_VIDEO_H263);
			 map.put("3g2",FFMpegOptions.CODEC_VIDEO_H263);
			
			 return map.get(format);
		 }
		
	 public static String getAudioCodecByFormat(String format){
		 Map<String,String> map=new HashMap<String,String>();
		 map.put("aac", "aac");	//but this is not supported pure_ffmpeg
		 map.put("mp2", "mp2");
		 map.put("mp3", "mp3");
		 map.put("ac3", "ac3");
		 
		 map.put("wav", FFMpegOptions.CODEC_AUDIO_PCM_S16LE);
		 map.put(FFMpegOptions.FORMAT_S16LE, FFMpegOptions.CODEC_AUDIO_PCM_S16LE);
		 //audio codecs.
		 
		 //TODO add video defaults
		 map.put("dvd", "mp2");
		 map.put("mov", "aac");//maybe
		 map.put("3gp", "aac");//maybe
		 map.put("3g2", "aac");//maybe
		
		 return map.get(format);
	 }
	 
	 public static String getAudioCodecByExtension(String extension){
		 Map<String,String> map=new HashMap<String,String>();
		 map.put("aac", "aac");
		 map.put("mp2", "mp2");
		 map.put("mp3", "mp3");
		 map.put("ac3", "ac3");
		 map.put("wav", FFMpegOptions.CODEC_AUDIO_PCM_S16LE);
		 map.put("lpcm", FFMpegOptions.CODEC_AUDIO_PCM_S16LE);//my special

		 
		 map.put("mov", "aac");//maybe
		 map.put("3gp", "aac");//maybe
		 map.put("3g2", "aac");//maybe
		
		
		 return map.get(extension);
	 }
	 /**
	  * @deprecated
	  * use akjava.com VideoUtils
	  */
	 public static int[] sizeToWidthHeight(String size){
		 String[] vs=size.split("x");
		 if(vs.length==1){
			 return null;
		 }else{
			 return new int[]{Integer.parseInt(vs[0]),Integer.parseInt(vs[1])};
		 }
	 }
		public static boolean isFFMpegAvailable(String path){
			if(path!=null && !path.equals("")){
				if(path.toLowerCase().endsWith("ffmpeg.exe") && new File(path).exists()){
				return true;
				}
			}
			return false;
		}
		
		

		//now only support 9133
		public static int getFFMpegGeneration(String path){
			
		int ret=2;//new ffmpeg is almost 2
			Runtime runtime=Runtime.getRuntime();
			String[] command=new String[]{path,"-formats"};
			
			
				Process process;
				try {
					process = runtime.exec(command);
					SimpleProcessPrinter printer=new SimpleProcessPrinter();
					ProcessMonitor monitor=new ProcessMonitor(process,printer);
					monitor.startMonitor();
					try {
						process.waitFor();
					} catch (InterruptedException e) {
						
						e.printStackTrace();
					}
			String formats=printer.getLog();
			
			//skip until Codecs:
			int start=formats.indexOf("Codecs".toLowerCase());
			if(start!=-1){
				formats=formats.substring(start);
			}
			int end=formats.indexOf("protocols:");
			if(end!=-1){
				formats=formats.substring(0,end);
			}
			
			String lines[]=formats.split("\n");
			
			boolean notfound=true;
			
			String checks[]={FFMpegOptions.CODEC_AUDIO_LIB_FAAC,FFMpegOptions.CODEC_AUDIO_LIB_MP3LAME,FFMpegOptions.CODEC_VIDEO_LIB_XVID,FFMpegOptions.CODEC_VIDEO_LIB_X264};
			LOOP:for(int j=0;j<lines.length;j++){
			for (int i = 0; i < checks.length; i++) {
				//some version have invalid white spaces
				if(StringUtils.deleteWhitespace(lines[j]).indexOf(checks[i])!=-1){
					
					notfound=false;
					
					break LOOP;
				}
			}
			
			
			}
			
			if(notfound){
				ret=1;
			}
				}catch (Exception e) {
					e.printStackTrace();
				}
			
			
			
			return ret;
		}
	/**
	 * @deprecated
	 * use AvisynthUtils.getAvisynthScript(path);
	 * @param path
	 * @return
	 */	
	public static String toAVSScript(String path){
	//	log.info(AvisynthUtils.getAvisynthScript(path));
		return AvisynthUtils.getAvisynthScript(path);
		//return "AviSource(\""+path+"\")";
	}
	/**
	 * 
	 */
	public static boolean shouldUseAvisynth(File file){
		String notsupport[]={"flv","mpg","mp3"};
		for (int i = 0; i < notsupport.length; i++) {
			if(file.getName().endsWith("."+notsupport[i])){
				return false;
			}
		}
		return true;
	}
	public static boolean isAvisynthAvailable(File ffmpeg){
		File tmp=null;
		try {
			tmp = File.createTempFile("avisynth",".avs");
			FileWriter writer=new FileWriter(tmp);
			writer.write("BlankClip()");
			writer.close();
			Runtime runtime=Runtime.getRuntime();
			Process p=runtime.exec(new String[]{ffmpeg.getAbsolutePath(),"-i",tmp.getAbsolutePath()});
			SimpleProcessPrinter ffmpegMonitor=new SimpleProcessPrinter();
			ProcessMonitor monitor=new ProcessMonitor(p,ffmpegMonitor);
			monitor.startMonitor();
			try {
				p.waitFor();
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			tmp.delete();
			String log=ffmpegMonitor.getLog();
			if(log.indexOf("Video: rawvideo")!=-1){
				return true;
			}
		} catch (IOException e) {
			// TODO Auto-generated catch block
			tmp.delete();
			e.printStackTrace();
		}
		return false;
	}
	

	public static boolean isProgressString(String line){
		return line.startsWith("size=") || line.startsWith("frame=");
	}
	
	
	public static int getProgressFrame(String line){
		int r=0;
		int s=line.indexOf("frame=");
		if(s!=-1){
			int end=line.indexOf("k",s+5);
			if(end!=-1){
				String tmp=line.substring(s+5,end);
				r=Integer.parseInt(tmp.replaceAll(" ",""));
			}
		}
		return r;
	}
	
	public static int getProgressSize(String line){
		
		int r=0;
		int s=line.indexOf("size=");
		if(s!=-1){
			int end=line.indexOf("k",s+5);
			if(end!=-1){
				
				String tmp=line.substring(s+5,end);
				//tmp=StringUtils.replace(tmp, "\t", "");//white space
				//log.info'"+tmp+"'");
				//log.info((int)tmp.charAt(0));
				r=Integer.parseInt(tmp.replaceAll(" ",""));
			}
		}else{
			
		}
		
		return r;
	}
	
public static long getProgressTime(String line){
		
		long r=0;
		int s=line.indexOf("time=");
		if(s!=-1){
			int end=line.indexOf(" ",s+5);
			if(end!=-1){
				
				String tmp=line.substring(s+5,end);
				//tmp=StringUtils.replace(tmp, "\t", "");//white space
				//log.info'"+tmp+"'");
				//log.info((int)tmp.charAt(0));
				r=(long) (Double.parseDouble(tmp.replaceAll(" ",""))*1000);
			}
		}else{
			
		}
		
		return r;
	}
	/**
	 * Bugged use akjava TimeUtils
	 * @deprecated
	 * @param duration
	 * @return
	 */
	public static double timeToDouble(String duration){
		if(duration==null){
			return 0;
		}
		Pattern durationPattern=Pattern.compile("(\\d+):(\\d+):(\\d+)\\.(\\d)+$");
		long millisecond=0;
		Matcher match=durationPattern.matcher(duration);
		
		if(match.matches()){
			int hour=Integer.parseInt(match.group(1));
			int minute=Integer.parseInt(match.group(2));
			int second=Integer.parseInt(match.group(3));
			//BUg fix for wrong millisecond
			String msString=match.group(4);
			while(msString.length()<3){
				msString+="0";
			}
			int millis=Integer.parseInt(msString);
			
			millisecond=hour*60*60*1000;
			millisecond+=minute*60*1000;
			millisecond+=second*1000;
			millisecond+=millis;
			
			return (double)millisecond/1000;
		}else{
			return 0;
		}
	}
	
	/**
	 * some ffmpeg cant handle mp4 files
	 * @param filename
	 * @return is mp4 or not
	 */
	public static boolean isMP4FileName(String name){
		String extesntion[]={"mp4","aac","m4v","m4a"};
		for (int i = 0; i < extesntion.length; i++) {
			if(name.toLowerCase().endsWith("."+extesntion[i])){
				return true;
			}
		}
		return false;
	}

	public static String toPrintCommand(String args[]){
		String out="";
		for (int j = 0; j < args.length; j++) {
			out+=("\""+args[j]+"\""+" ");
		}
		return out;
	}
	
	public static String audioCodecToExtension(String audioCodec){
			if(audioCodec.equals(FFMpegOptions.CODEC_AUDIO_MP2)){
			return "mp2";	
			}else if(audioCodec.equals(FFMpegOptions.CODEC_AUDIO_WAV)){
			return "wav";	
			}else if(audioCodec.equals(FFMpegOptions.CODEC_AUDIO_WMA1)){
			return "wma";	
			}else if(audioCodec.equals(FFMpegOptions.CODEC_AUDIO_WMA2)){
			return "wma";	
			}else if(audioCodec.equals(FFMpegOptions.CODEC_AUDIO_AC3)){
			return "ac3";	
			}else if(audioCodec.equals(FFMpegOptions.CODEC_AUDIO_AAC)){
			return "aac";	//warn PSP prefer .mp4
			}
			return null;
			
	}
	public static String fixOddSize(int w,int h){
		String ret="";
		if(w>0){
			if(w%2>0){
				ret+=(w-1)+"x";
			}else{
				ret+=(w)+"x";
			}
		}else{
			return null;
		}
		
		if(h>0){
			if(h%2>0){
				ret+=(h-1);
			}else{
				ret+=(h);
			}
		}else{
			return null;
		}
		return ret;
	}
	public static boolean isOdd(int w,int h){
		boolean ret=false;
		if(w>0){
			if(w%2>0){
				return true;
			}
		}
		
		if(h>0){
			if(h%2>0){
				return true;
			}
		}
		return ret;
	}
	
	/**
	 * 
	 * @deprecated use TimeUtils
	 * @return
	 */
	
	public static String toTimeLabel(long millisecond){
		long hour_millisecond=(60*60*1000);
		int minute_millisecond=60*1000;
		int second_millisecond=1000;
		int hour=(int)(millisecond/hour_millisecond);
		long remain=millisecond%hour_millisecond;
		int minute=(int)(remain/minute_millisecond);
		remain=remain%minute_millisecond;
		int second=(int)(remain/second_millisecond);
		//remain=remain%second_millisecond;
		
		
		return toLabel2(hour)+":"+toLabel2(minute)+":"+toLabel2(second);
}
	/**
	 * 
	 * @deprecated use TimeUtils
	 * @return
	 */
	public static String toMillisecondLabel(long millisecond){
		long hour_millisecond=(60*60*1000);
		int minute_millisecond=60*1000;
		int second_millisecond=1000;
		int hour=(int)(millisecond/hour_millisecond);
		long remain=millisecond%hour_millisecond;
		int minute=(int)(remain/minute_millisecond);
		remain=remain%minute_millisecond;
		int second=(int)(remain/second_millisecond);
		remain=remain%second_millisecond;
		
		if(hour>0){
		return toLabel2(hour)+":"+toLabel2(minute)+":"+toLabel2(second)+"."+remain;
		}else{
		return toLabel2(minute)+":"+toLabel2(second)+"."+remain;	
		}
}
	
	private static String toLabel2(int number){
		String v=""+number;
		if(v.length()<2)
		v="0"+v;
		return v;
	}
	
	public static String getWaveAudioFormat(File file){
		String format=null;
		AudioInputStream ainput;
		try {
			format="";
			ainput = AudioSystem.getAudioInputStream(file);
			AudioFormat aformat=ainput.getFormat();
			if(aformat.getEncoding().equals(Encoding.PCM_SIGNED)){
				format+="s";
			}else if(aformat.getEncoding().equals(Encoding.PCM_UNSIGNED)){
				format+="u";
			}else{
				return null;
			}
			format+=aformat.getSampleSizeInBits();
			if(aformat.isBigEndian()){
				format+="be";
			}else{
				format+="le";
			}
			ainput.close();
		} catch (UnsupportedAudioFileException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
		return format;
	}
	
	public static String pcmFormatToCodec(String format){
		return "pcm_"+format;
	}
	public static String pcmCodecToFormat(String codec){
		if(codec.startsWith("pcm_")){
			return codec.substring("pcm_".length());
		}
		return null;
	}
	
	public static String toLabeledByte(long bytes){
		
		if(bytes<1024){
			return bytes+" Bytes";
		}else if(bytes<1024*1024){//kbyte
			BigDecimal bd2 = new BigDecimal((double)bytes/1024);
			//kbyte to simple
			double ret = bd2.setScale(0, BigDecimal.ROUND_HALF_UP)
			                  .doubleValue();
			return ret+" KBytes";
		}else if(bytes<1024*1024*1024){//Mbyte
			BigDecimal bd2 = new BigDecimal((double)bytes/1024/1024);
			
			double ret = bd2.setScale(1, BigDecimal.ROUND_HALF_UP)
			                  .doubleValue();
			return ret+" MBytes";
		}else{
BigDecimal bd2 = new BigDecimal((double)bytes/1024/1024/1024);
			
			double ret = bd2.setScale(2, BigDecimal.ROUND_HALF_UP)
			                  .doubleValue();
			return ret+" GBytes";
		}
	}
	
	
}
